04 - Types de données et transformations

PRO1036 - Analyse de données scientifiques en R

Tim Bollé

October 6, 2025

Pourquoi s’intéresser aux types de données ?

Exemple: Cat lovers

Un sondage a demandé à des gens leur nom et le nombre de chat qu’ils possèdent. Les instructions indiquaient d’entrer le nombre de chats en chiffres.


cat_lovers <- read_csv("data/cat-lovers.csv")
# A tibble: 60 × 3
   name           number_of_cats handedness
   <chr>          <chr>          <chr>     
 1 Bernice Warren 0              left      
 2 Woodrow Stone  0              left      
 3 Willie Bass    1              left      
 4 Tyrone Estrada 3              left      
 5 Alex Daniels   3              left      
 6 Jane Bates     2              left      
 7 Latoya Simpson 1              left      
 8 Darin Woods    1              left      
 9 Agnes Cobb     0              left      
10 Tabitha Grant  0              left      
# ℹ 50 more rows

Statistique simple…

cat_lovers %>%
  summarise(mean_cats = mean(number_of_cats))
# A tibble: 1 × 1
  mean_cats
      <dbl>
1        NA

Ca ne marche pas…

Pourquoi ?

?mean

Et maintenant ?

cat_lovers %>%
  summarise(mean_cats = mean(number_of_cats, na.rm = TRUE))
# A tibble: 1 × 1
  mean_cats
      <dbl>
1        NA

Toujours pas…

Regardons les données

glimpse(cat_lovers)
Rows: 60
Columns: 3
$ name           <chr> "Bernice Warren", "Woodrow Stone", "Willie Bass", "Tyro…
$ number_of_cats <chr> "0", "0", "1", "3", "3", "2", "1", "1", "0", "0", "0", …
$ handedness     <chr> "left", "left", "left", "left", "left", "left", "left",…

Il suffit de convertir !

cat_lovers %>%
  mutate(
    number_of_cats = as.numeric(number_of_cats)
  ) %>%
  summarise(mean_cats = mean(number_of_cats, na.rm = TRUE))
# A tibble: 1 × 1
  mean_cats
      <dbl>
1     0.776

Ok mais …

Warning: There was 1 warning in `mutate()`.
ℹ In argument: `number_of_cats = as.numeric(number_of_cats)`.
Caused by warning:
! NAs introduits lors de la conversion automatique

Regardons cela de plus près…

Respecter les types de données

cat_lovers %>%
  mutate(
    number_of_cats = case_when(
      name == "Ginger Clark" ~ "2",
      name == "Doug Bass"    ~ "3",
      TRUE                   ~ number_of_cats
      ),
    number_of_cats = as.numeric(number_of_cats)
    ) %>%
  summarise(mean_cats = mean(number_of_cats))
# A tibble: 1 × 1
  mean_cats
      <dbl>
1     0.833

Enregistrer

cat_lovers <- cat_lovers %>%
  mutate(
    number_of_cats = case_when(
      name == "Ginger Clark" ~ "2",
      name == "Doug Bass"    ~ "3",
      TRUE                   ~ number_of_cats
      ),
    number_of_cats = as.numeric(number_of_cats)
    )

Morale

  • Si vos données ne se comportent pas comme vous l’attendez, il se peut qu’il s’agisse d’un probplème de type de données.
  • Explorez et investiguez vos données, appliquez les modifications, sauvegardez vos données, vivez heureux

Types de données

Types de données dans R

  • logical
  • double
  • integer
  • character
  • Il y a en d’autres mais nous les utiliserons peu

Logical et character

logical - Valeurs booléennes TRUE et FALSE

typeof(TRUE)
[1] "logical"


character - Texte

typeof("Hello")
[1] "character"

Double et integer

double - Nombres à virgule flottante (type par défaut pour les nombres)

typeof(1.5)
[1] "double"
typeof(7)
[1] "double"


integer - Nombres entiers (indiqué par un L)

typeof(1L)
[1] "integer"
typeof(1:3)
[1] "integer"

Concatenation

Des vecteurs peuvent être construits à l’aide de la fonction c()


c(1, 2, 3)
[1] 1 2 3
c("Hello", "World!")
[1] "Hello"  "World!"
c(c("hi", "hello"), c("bye", "jello"))
[1] "hi"    "hello" "bye"   "jello"

Conversion

… intentionnelle

x <- 1:3
x
[1] 1 2 3
typeof(x)
[1] "integer"
y <- as.character(x)
y
[1] "1" "2" "3"
typeof(y)
[1] "character"


x <- c(TRUE, FALSE)
x
[1]  TRUE FALSE
typeof(x)
[1] "logical"
y <- as.numeric(x)
y
[1] 1 0
typeof(y)
[1] "double"

Conversion

… accidentelle

R va faire les conversions sans se poser de questions, surtout quand on mets différentes choses dans un même vecteur.

c(1, "Hello")
[1] "1"     "Hello"
c(FALSE, 3L)
[1] 0 3
c(1.2, 3L)
[1] 1.2 3.0
c(2L, "two")
[1] "2"   "two"

Conversion

  • Explicite - Utilisez les fonctions as.*()
    • as.logical()
    • as.numeric()
    • as.integer()
    • as.character()
    • as.double()
  • Implicite - R va faire les conversions pour vous, soyez vigilant

Cas spéciaux

Cas spéciaux

  • NA - Valeur manquante
  • Inf - Infini
  • NaN - Not a Number


# division par zéro -> Inf
7/0
[1] Inf
-1/0
[1] -Inf


Inf - Inf
[1] NaN

NA

x <- c(1, 2, 3, 4, NA)
mean(x)
[1] NA
mean(x, na.rm = TRUE)
[1] 2.5
summary(x)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   1.00    1.75    2.50    2.50    3.25    4.00       1 

NA

Les NA sont utilisés par R pour représenter des valeurs manquantes. Ils sont de type logique.


typeof(NA)
[1] "logical"


# TRUE or NA
TRUE | NA
[1] TRUE
# FALSE or NA
FALSE | NA
[1] NA

Classes de données

Classes de données

Nous avons parlé des types des données et nous allons maintenant parler des classes des données:

  • Vecteurs sont comme des briques LEGO
  • On les colle ensemble pour construire des structures plus compliquées, notamment des représentations de données

Par exemples:

  • factor
  • date
  • data frame

factor

Les facteurs sont utilisés pour gérer les variables catégorielles, c’est-à-dire les variables qui ont un ensemble fixe et connu de valeurs possibles.


x <- factor(c("BS", "MS", "PhD", "MS"))
x
[1] BS  MS  PhD MS 
Levels: BS MS PhD


typeof(x)
[1] "integer"
class(x)
[1] "factor"

factor

Pour chaque facteur on a:

  • levels - Les valeurs possibles
  • labels - Les étiquettes associées aux valeurs


glimpse(x)
 Factor w/ 3 levels "BS","MS","PhD": 1 2 3 2
as.integer(x)
[1] 1 2 3 2

Dates

y <- as.Date("2024-01-01")
y
[1] "2024-01-01"
typeof(y)
[1] "double"
class(y)
[1] "Date"

Dates

Les dates sont en réalité un entier (le nombre de jours depuis l’origine, 1 Jan 1970) et un entier (l’origine) collés ensemble

as.integer(y)
[1] 19723
as.integer(y) / 365 
[1] 54.03562

Data frames

Les data frames sont des structures de données qui sont comme des vecteurs de différentes classes.

df <- data.frame(x = 1:2, y = 3:4)
df
  x y
1 1 3
2 2 4
typeof(df)
[1] "list"
class(df)
[1] "data.frame"

Listes

Les listes sont des conteneurs génériques pour des vecteurs de n’importe quel type.

l <- list(
  x = 1:4,
  y = c("hi", "hello", "jello"),
  z = c(TRUE, FALSE)
)
l
$x
[1] 1 2 3 4

$y
[1] "hi"    "hello" "jello"

$z
[1]  TRUE FALSE

Listes et data frames

  • Un data frame est une liste spéciale contenant des vecteurs de longueur égale
  • Un tibble est un data frame du Tidyverse
df
  x y
1 1 3
2 2 4
df %>%
  as_tibble()
# A tibble: 2 × 2
      x     y
  <int> <int>
1     1     3
2     2     4
df %>%
  as_tibble() %>%
  class()
[1] "tbl_df"     "tbl"        "data.frame"

Tibbles

Les Tibbles sont des data frames modernes. Ils sont adaptés au tidyverse (en pratique, toutes les fonctions du tidyverse retournent des tibbles au lieu de data.frame).

  • Les tibbles ont un affichage limité, adapté à la console
  • Il est possible d’accéder à une colonne à l’aide des double crochets (df[["x"]]) en plus du $ (df$x). Il est ainsi possible d’utiliser le numéro de colonne au lieu du nom (df[[1]])
  • Les tibbles fournissent également plus d’indications lorsque quelque chose ne va pas (messages de warnings).

Travailler avec des facteurs

Exemple: Cat lovers

Nous commençons avec un data frame avec des caractères

glimpse(cat_lovers)
Rows: 60
Columns: 3
$ name           <chr> "Bernice Warren", "Woodrow Stone", "Willie Bass", "Tyro…
$ number_of_cats <chr> "0", "0", "1", "3", "3", "2", "1", "1", "0", "0", "0", …
$ handedness     <chr> "left", "left", "left", "left", "left", "left", "left",…

Plotting

Au moment de faire un graphique, R va faire une conversion de type

Si on lui demande de plotter des catégories, il va créer des facteurs

ggplot(cat_lovers, mapping = aes(x = handedness)) +
  geom_bar()

Les facteurs sont ordonnées par ordre alphabétique par défaut

On peut manipuler les facteurs avec forcats

cat_lovers %>%
  mutate(handedness = fct_infreq(handedness)) %>%
  ggplot(mapping = aes(x = handedness)) +
  geom_bar()

La grammaire de la manipulation de données

Basé sur des fonctions qui correspondent à des verbes permettant de manipuler des dataframes.

  • Les facteurs sont utiles lorsque vous avez des données catégorielles et que vous voulez remplacer l’ordre des vecteurs de caractères pour améliorer l’affichage
  • Le package forcats fournit une suite d’outils utiles qui résolvent des problèmes courants avec les facteurs

Les fonctions de forcats

focats possède plusieurs fonctions pour manipuler les facteurs:

  • fct_reorder() - Réordonne les niveaux d’un facteur en fonction d’une autre variable
  • fct_relevel() - Réordonne les niveaux d’un facteur
  • fct_infreq() - Réordonne les niveaux d’un facteur en fonction de leur fréquence
  • fct_lump() - Regroupe les niveaux d’un facteur en “autres”
  • fct_explicit_na() - Ajoute un niveau pour les valeurs manquantes

Exemple: starwars

starwars %>%
  ggplot(aes(y = hair_color)) + 
  geom_bar()

Ordre selon la fréquence : fct_infreq()

starwars %>%
  mutate(
    hair_color = fct_infreq(hair_color)
  ) %>%
  ggplot(aes(y = hair_color)) + 
  geom_bar()

Regroupement de facteurs

Utile quand on a beaucoup de niveaux

starwars %>%
  count(skin_color, sort = TRUE)
# A tibble: 31 × 2
   skin_color     n
   <chr>      <int>
 1 fair          17
 2 light         11
 3 dark           6
 4 green          6
 5 grey           6
 6 pale           5
 7 brown          4
 8 blue           2
 9 blue, grey     2
10 none           2
# ℹ 21 more rows

… 31 niveaux dans cet exemple !

Regroupement de facteurs : fct_lump()

starwars %>%
  mutate(skin_color = fct_lump(skin_color, n = 5)) %>%
  count(skin_color, sort = TRUE)
# A tibble: 6 × 2
  skin_color     n
  <fct>      <int>
1 Other         41
2 fair          17
3 light         11
4 dark           6
5 green          6
6 grey           6

Regroupement de facteurs

Regardons maintenant la masse moyenne selon la couleur des yeux:

starwars %>%
  mutate(eye_color = fct_lump(eye_color, n = 6)) %>%
  group_by(eye_color) %>%
  summarise(mean_mass = mean(mass, na.rm = TRUE)) %>%
  ggplot(aes(x = eye_color, y = mean_mass)) +
  geom_col()

Regroupement et changement d’ordre !

Nous allons maintenant regrouper les couleurs des yeux et les ordonner selon la masse moyenne

starwars %>%
  mutate(eye_color = fct_lump(eye_color, n = 6)) %>%
  group_by(eye_color) %>%
  summarise(mean_mass = mean(mass, na.rm = TRUE)) %>%
  mutate(eye_color = fct_reorder(eye_color, mean_mass)) %>%
  ggplot(aes(x = eye_color, y = mean_mass)) +
  geom_col()

Exemple: Hotels

Exemple: Hotels

Choix manuel de l’ordre : fct_relevel()

La variable month.name est une variable intégrée dans R qui contient les noms des mois en anglais et dans le bon ordre.

hotels <- readr::read_csv("data/hotels.csv")

hotels %>%
  mutate(arrival_date_month = fct_relevel(arrival_date_month, month.name)) %>% # On réordonne les mois
  group_by(hotel, arrival_date_month) %>%   # group by type d'hotel et mois d'arrivée
  summarise(mean_adr = mean(adr)) %>%       # calcul de l'ADR moyen pour chaque groupe
  ggplot(aes(
    x = arrival_date_month,
    y = mean_adr,                           # mean_adr sur y-axis
    group = hotel,                          # Groupe les lignes par type
    color = hotel)                          # couleur par type
    ) +
  geom_line() +                             # On veut des lignes
  scale_y_continuous(labels = label_dollar()) +
  theme_minimal() +                         # Utilise le minimal theme
  labs(x = "Arrival month",                 # On met à jour les labels
       y = "Mean ADR (average daily rate)",
       title = "Comparison of resort and city hotel prices across months",
       subtitle = "Resort hotel prices soar in the summer while city hotel prices remain\nrelatively constant throughout the year",
       color = "Hotel type") +
  scale_x_discrete(guide = guide_axis(check.overlap = TRUE)) # On s'assure que les labels ne se chevauchent pas

Renommer les niveaux : fct_recode()

Si on veut simplement renommer les niveaux, on peut utiliser fct_recode()

cat_lovers %>%
  mutate(handedness = fct_recode(handedness, gaucher = "left", droitier = "right")) %>%
  mutate(handedness = fct_infreq(handedness)) %>%
  ggplot(mapping = aes(x = handedness)) +
  geom_bar()

Travailler avec les dates

Dates

  • lubridate est un package tidyverse-friendly qui facilite la manipulation des dates

  • Il ne fait pas partie du coeur tidyverse, donc il doit être installé avec install.packages("lubridate") mais il n’est pas chargé avec lui, et doit être explicitement chargé avec library(lubridate).

    • …

Exemple des hotels

Calculer et visualiser le nombre de réservations à une date d’arrivée donnée

Nous allons voir les bases mais travailler avec des dates peut s’avérer complexe mais cela peut apporter beaucoup à une analyse.

Étape 1 - Construire la date

library(glue) # glue permet de coller des éléments ensemble

hotels %>%
  mutate(
    arrival_date = glue("{arrival_date_year} {arrival_date_month} {arrival_date_day_of_month}")
    ) %>% 
  relocate(arrival_date) # pour afficher la nouvelle colonne à gauche
# A tibble: 119,390 × 33
   arrival_date hotel is_canceled lead_time arrival_date_year arrival_date_month
   <glue>       <chr>       <dbl>     <dbl>             <dbl> <chr>             
 1 2015 July 1  Reso…           0       342              2015 July              
 2 2015 July 1  Reso…           0       737              2015 July              
 3 2015 July 1  Reso…           0         7              2015 July              
 4 2015 July 1  Reso…           0        13              2015 July              
 5 2015 July 1  Reso…           0        14              2015 July              
 6 2015 July 1  Reso…           0        14              2015 July              
 7 2015 July 1  Reso…           0         0              2015 July              
 8 2015 July 1  Reso…           0         9              2015 July              
 9 2015 July 1  Reso…           1        85              2015 July              
10 2015 July 1  Reso…           1        75              2015 July              
# ℹ 119,380 more rows
# ℹ 27 more variables: arrival_date_week_number <dbl>,
#   arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
#   stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
#   meal <chr>, country <chr>, market_segment <chr>,
#   distribution_channel <chr>, is_repeated_guest <dbl>,
#   previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>, …

Étape 2 - Compter les réservations par dates

hotels %>%
  mutate(arrival_date = glue("{arrival_date_year} {arrival_date_month} {arrival_date_day_of_month}")) %>%
  count(arrival_date)
# A tibble: 793 × 2
   arrival_date       n
   <glue>         <int>
 1 2015 August 1    110
 2 2015 August 10   207
 3 2015 August 11   117
 4 2015 August 12   133
 5 2015 August 13   107
 6 2015 August 14   329
 7 2015 August 15   190
 8 2015 August 16    98
 9 2015 August 17   188
10 2015 August 18    94
# ℹ 783 more rows

Étape 4 - Visualiser

hotels %>%
  mutate(arrival_date = glue("{arrival_date_year} {arrival_date_month} {arrival_date_day_of_month}")) %>%
  count(arrival_date) %>%
  ggplot(aes(x = arrival_date, y = n, group = 1)) +
  geom_line()

Quel est le problème ici ?

Étape 1 - Construire la date (comme une date)

library(lubridate) # pour les dates

hotels %>%
  mutate(
    arrival_date = ymd(glue("{arrival_date_year} {arrival_date_month} {arrival_date_day_of_month}"))
    ) %>% 
  relocate(arrival_date)
# A tibble: 119,390 × 33
   arrival_date hotel is_canceled lead_time arrival_date_year arrival_date_month
   <date>       <chr>       <dbl>     <dbl>             <dbl> <chr>             
 1 2015-07-01   Reso…           0       342              2015 July              
 2 2015-07-01   Reso…           0       737              2015 July              
 3 2015-07-01   Reso…           0         7              2015 July              
 4 2015-07-01   Reso…           0        13              2015 July              
 5 2015-07-01   Reso…           0        14              2015 July              
 6 2015-07-01   Reso…           0        14              2015 July              
 7 2015-07-01   Reso…           0         0              2015 July              
 8 2015-07-01   Reso…           0         9              2015 July              
 9 2015-07-01   Reso…           1        85              2015 July              
10 2015-07-01   Reso…           1        75              2015 July              
# ℹ 119,380 more rows
# ℹ 27 more variables: arrival_date_week_number <dbl>,
#   arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
#   stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
#   meal <chr>, country <chr>, market_segment <chr>,
#   distribution_channel <chr>, is_repeated_guest <dbl>,
#   previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>, …

Étape 2 - Compter les réservations par dates

hotels %>%
  mutate(arrival_date = ymd(glue("{arrival_date_year} {arrival_date_month} {arrival_date_day_of_month}"))) %>% 
  count(arrival_date)
# A tibble: 793 × 2
   arrival_date     n
   <date>       <int>
 1 2015-07-01     122
 2 2015-07-02      93
 3 2015-07-03      56
 4 2015-07-04      88
 5 2015-07-05      53
 6 2015-07-06      75
 7 2015-07-07      54
 8 2015-07-08      69
 9 2015-07-09      80
10 2015-07-10      51
# ℹ 783 more rows

Étape 3a - Visualiser

hotels %>%
  mutate(arrival_date = ymd(glue("{arrival_date_year} {arrival_date_month} {arrival_date_day_of_month}"))) %>% 
  count(arrival_date) %>%
  ggplot(aes(x = arrival_date, y = n, group = 1)) +
  geom_line()

Étape 3b - Visualiser avec une courbe

hotels %>%
  mutate(arrival_date = ymd(glue("{arrival_date_year} {arrival_date_month} {arrival_date_day_of_month}"))) %>% 
  count(arrival_date) %>%
  ggplot(aes(x = arrival_date, y = n, group = 1)) +
  geom_smooth() #<<